package main

import (
	"bytes"
	. "excel-fyne/cache"
	"excel-fyne/config"
	"excel-handle/wrap"
	"fmt"
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/data/binding"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
	"gopkg.in/yaml.v3"
	"os"
	"path/filepath"
	"sync"
	"time"
)

func main() {

	// 读取缓存的配置
	var CustomPropPath = ".\\res\\prop-custom.yaml"
	var AbsCustomPropPath, _ = filepath.Abs(CustomPropPath)
	// 默认配置路径
	var DefaultPropFilePath = wrap.DefaultPropFilePath

	// 实际读取的配置路径
	var ActualPropFilePath = DefaultPropFilePath
	// 默认路径
	// ActualPropFilePath := util.Or(CustomPropPath, DefaultPropFilePath)
	// Prop 读取配置
	var Prop, _ = wrap.ReadPropFile(ActualPropFilePath)
	// Members 成员选择【指针测试】
	var MembersPtr *[]string

	// 当prop地址变化（被重新赋值）时，需要手动同步子属性指针。
	SyncPropFieldsPtr := func() {
		MembersPtr = &(Prop.DailyDuty.Members)
	}

	// 初始化子属性指针
	SyncPropFieldsPtr()

	// fyne app 初始化配置
	fmt.Println("程序已启动")
	// 初始化配置 GUI 界面
	// 自定义中文字体
	config.LoadCustomFyneConfig()
	// Preferences API requires a unique ID, use app.NewWithID() or the FyneApp.toml ID field to instead of app.New()
	a := app.NewWithID("elegy.excel-fyne")
	w := a.NewWindow("Hello Excel-Generator-Fyne")

	// 界面相关部件
	var ThemeToggleButton *widget.Button
	ThemeToggleButton = widget.NewButton("", nil)
	ThemeToggleButton.Text = "切换主题"
	ThemeToggleButton.Icon = theme.ColorPaletteIcon()

	switchTheme := func() {
		if a.Settings().ThemeVariant() == theme.VariantLight {
			a.Settings().SetTheme(theme.DarkTheme())
			return
		}
		if a.Settings().ThemeVariant() == theme.VariantDark {
			a.Settings().SetTheme(theme.LightTheme())
		}
	}

	ThemeToggleButton.OnTapped = switchTheme

	// 定义部件
	var MembersList *widget.List                // 成员列表
	var FirstTRMenember *widget.Select          // 第一个丢垃圾成员选择
	var FirstDCMenember *widget.Select          // 第一个搞卫生成员选择
	var AddMemberEntry *widget.Entry            // 添加成员输入框
	var AddMemberButton *widget.Button          // 添加成员按钮
	var DeleteMemberSelect *widget.Select       // 删除成员选择
	var DeleteMemberButton *widget.Button       // 删除成员按钮
	var MonthSelect *widget.Select              // 月份选择
	var ReloadDefaultPropButton *widget.Button  // 加载默认配置按钮
	var SaveCustomPropButton *widget.Button     // 加载缓存的个人配置按钮
	var LoadCustomPropButton *widget.Button     // 加载缓存的个人配置按钮
	var DeleteCustomPropButton *widget.Button   // 删除缓存的个人配置按钮
	var UndoAddOrDelMemberButton *widget.Button // 撤销添加或删除成员按钮

	// 定义方法：判断个人配置文件是否存在
	// 配合通知
	isCustomPropFileExist := func() bool {
		//它仅仅检查文件的状态，包括文件是否存在。尝试打开文件，比os.Open节约资源
		if _, err := os.Stat(CustomPropPath); os.IsNotExist(err) {
			return false
		}
		return true
	}

	// 个人配置文件缓存状态
	//var isCustomPropFileExistCache bool

	var CustomPropCache = Prop

	propChangesLock := sync.Mutex{}

	var isCustomPropChanged = func() bool {
		propChangesLock.Lock()
		propChangesLock.Unlock()

		cacheBytes, _ := yaml.Marshal(CustomPropCache)
		propBytes, _ := yaml.Marshal(Prop)
		// 相等，未变更，返回false，不执行覆盖
		if bytes.Equal(cacheBytes, propBytes) {
			return false
		}
		return true
	}

	updateCustomPropStatusLock := sync.Mutex{}

	// 更新加载个人配置按钮状态的函数：加载、删除
	// 保存是always ok的
	// 此方法至变更按钮状态
	updateCustomPropButtonStatus := func() {
		updateCustomPropStatusLock.Lock()
		updateCustomPropStatusLock.Unlock()

		// 个人配置文件不存在
		if !isCustomPropFileExist() {
			// 直接赋值属性的话需要额外的刷新组件。初始化后是会默认刷新组件的！！
			SaveCustomPropButton.SetText("保存个人配置")
			LoadCustomPropButton.Disable()
			DeleteCustomPropButton.Disable()
		} else {
			SaveCustomPropButton.SetText("覆盖个人配置")
			LoadCustomPropButton.Enable()
			DeleteCustomPropButton.Enable()
		}
	}

	CustomPropOpsLock := sync.Mutex{}

	var AutoLockCustomPropOpsAndUpdateStatus = func(operation func()) {
		CustomPropOpsLock.Lock()
		defer CustomPropOpsLock.Unlock()

		LoadCustomPropButton.Disable()
		DeleteCustomPropButton.Disable()

		operation()

		updateCustomPropButtonStatus()
	}

	// 无缓存则禁用回退按钮
	DisableUndoButtonIfNoCache := func() {
		if len(UndoMemberCacheList) == 0 {
			UndoAddOrDelMemberButton.Disable()
			//UndoAddOrDelMemberButton.SetText("回退成员变更（暂无成员变更）")
		}
	}

	// 有缓存则启用回退按钮
	EnableUndoButtonIfHaveCache := func() {
		if len(UndoMemberCacheList) > 0 {
			UndoAddOrDelMemberButton.Enable()
			//UndoAddOrDelMemberButton.SetText("回退成员变更")
		}
	}

	// 预定义局部函数
	// 刷新成员列表。当member变更后调用
	var RefreshAfterMembersChanged = func() {

		fmt.Println("你看看prop啊啊啊")
		for i := range *MembersPtr {
			fmt.Println((*MembersPtr)[i])
		}
		fmt.Println()
		for i := range *MembersPtr {
			fmt.Println((*MembersPtr)[i])
		}
		// 这个members是同步至prop缓存的
		MembersList.Refresh()                      // 刷新列表展示
		DeleteMemberSelect.SetOptions(*MembersPtr) // 手动更新删除时的列表项
		FirstTRMenember.SetOptions(*MembersPtr)    // 手动更新第一个丢垃圾成员选择列表
		FirstDCMenember.SetOptions(*MembersPtr)    // 手动更新第一个搞卫生成员选择列表
		// 判断是否可回退
		DisableUndoButtonIfNoCache()
	}

	// 重新从文件中加载配置
	// 需要先加载指定的配置文件到prop，再应用prop至GUI
	ApplyToGuiAfterLoadedProp := func() {

		// 回显数据
		yaml.NewEncoder(os.Stdout).Encode(Prop)

		// 同步配置属性指针
		SyncPropFieldsPtr()
		// 填充prop中的数据至界面
		FirstTRMenember.SetSelected(Prop.DailyDuty.MonthFirstThrowRubbishMember)
		FirstDCMenember.SetSelected(Prop.DailyDuty.MonthFirstDoCleanMember)
		MonthSelect.SetSelectedIndex(Prop.DailyDuty.Month - 1)
		// 刷新成员列表
		RefreshAfterMembersChanged()
		// 清空回退缓存
		UndoMemberCacheList = []UndoMemberCache{}
		DisableUndoButtonIfNoCache()
		// 其他选项置空
		AddMemberEntry.SetText("")
		DeleteMemberSelect.ClearSelected()
		// 重载prop后配置后，重置判定自定义配置按钮的状态
		//（非必须） 主要是初始化，或是删除后加载默认配置时
		updateCustomPropButtonStatus()
	}

	// 定义添加回退成员变更缓存的方法
	AddUndoMemberCache := func(member string, option Opt) {
		cache := UndoMemberCache{
			Member: member,
			Option: option,
		}

		// 添加缓存项
		UndoMemberCacheList = append(UndoMemberCacheList, cache)
		// 启用按钮
		EnableUndoButtonIfHaveCache()
	}

	// 执行回退
	executeUndoMember := func() {
		lastCache := UndoMemberCacheList[len(UndoMemberCacheList)-1]
		switch lastCache.Option {
		case Add:
			{
				selfRemoveElement(MembersPtr, lastCache.Member)
			}
		case Del:
			{
				selfAppendElement(MembersPtr, lastCache.Member)
				//Prop.DailyDuty.Members = append(Prop.DailyDuty.Members, lastCache.Member)
			}
		}
		// 去除最后一个缓存
		UndoMemberCacheList = UndoMemberCacheList[:len(UndoMemberCacheList)-1]
		// 最后刷新显示
		RefreshAfterMembersChanged()
	}

	// 进度条
	//progressBar := widget.NewProgressBar()
	//progressBar.Max = 100
	//progressBar.Hide()

	infiniteWhenGenerating := widget.NewProgressBarInfinite()
	infiniteWhenGenerating.Hide()

	// 简单标签
	hello := widget.NewLabel("Hello Fyne!")
	var lableFlag bool
	helloButton := widget.NewButton("Hi!", func() {
		if lableFlag {
			hello.SetText("Hello Fyne!")
		} else {
			// 初次先执行
			hello.SetText("Welcome :)")
		}
		lableFlag = !lableFlag
	})

	// 窗口焦点变化事件
	// 输入框事件

	// 列表展示 声明
	MembersList = widget.NewList(
		func() int { return len(*MembersPtr) }, // 返回切片的长度
		func() fyne.CanvasObject {
			return container.NewHBox(
				widget.NewLabel(""),
				layout.NewSpacer())
			//widget.NewButton("", nil))
			// 占位符，稍后在更新函数中替换)
		}, // 创建列表项的模板
		// 也就是 Refresh 时调用的方法
		func(id widget.ListItemID, item fyne.CanvasObject) { // 更新列表项的内容

			// 类型断言语法
			// 它用于告诉编译器你确信变量 item 的动态类型是 *T（即 T 类型的指针）
			box := item.(*fyne.Container)

			// 更新标签
			// 构建带有序号的文本  id 是从0开始的
			labelText := fmt.Sprintf("%d. %s", id+1, (*MembersPtr)[id])
			box.Objects[0].(*widget.Label).SetText(labelText)
			// 更新按钮或弹出菜单
			//menuButton := widget.NewButtonWithIcon("选项", theme.MenuExpandIcon(), nil)
			//menuButton.OnTapped = func() {
			//	// 弹出菜单的逻辑
			//	fmt.Println("点击菜单：", id+1)
			//	//popUpMenu.Show()
			//	popUpMenu.ShowAtRelativePosition(fyne.Position{X: 0, Y: 0}, menuButton)
			//}
			//box.Objects[2] = menuButton
		},
	)

	// 第一丢垃圾
	FirstTRMenember = widget.NewSelect(*MembersPtr, nil)
	FirstTRMenember.PlaceHolder = "从所有成员中选择" // 占位符提示文本
	// 设置默认选值
	FirstTRMenember.SetSelected(Prop.DailyDuty.MonthFirstThrowRubbishMember)
	// 变更时缓存至prop
	FirstTRMenember.OnChanged = func(s string) {
		Prop.DailyDuty.MonthFirstThrowRubbishMember = s
	}

	// 第一搞卫生
	FirstDCMenember = widget.NewSelect(*MembersPtr, nil)
	FirstDCMenember.PlaceHolder = "从所有成员中选择" // 占位符提示文本
	// 设置默认选值
	FirstDCMenember.SetSelected(Prop.DailyDuty.MonthFirstDoCleanMember)
	// 变更时缓存至prop
	FirstDCMenember.OnChanged = func(s string) {
		Prop.DailyDuty.MonthFirstDoCleanMember = s
	}

	// 月份
	var months []string
	for i := 0; i < 12; i++ {
		months = append(months, fmt.Sprintf("%d月", i+1))
	}
	MonthSelect = widget.NewSelect(months, nil)
	// 设置默认选择的月份
	MonthSelect.SetSelectedIndex(Prop.DailyDuty.Month - 1)
	MonthSelect.OnChanged = func(s string) {
		// 获取当前选中的月份
		selectedMonth := MonthSelect.SelectedIndex() + 1
		// 变更时缓存至prop
		Prop.DailyDuty.Month = selectedMonth

		// 返回字节数，而不是字符数量。对于UTF-8编码的字符串，一个汉字可能占用3或4个字节
		//monthIntStr := s[:len(s)-1]
		//runes := []rune(s)
		//monthIntStr := string(runes[:len(runes)-1])
		//m, _ := strconv.Atoi(monthIntStr)
		//Prop.DailyDuty.Month = m
	}

	// 成员操作缓存按钮设置
	UndoAddOrDelMemberButton = widget.NewButtonWithIcon("回退成员变更", theme.ContentUndoIcon(), nil)
	// 初始化：无缓存则禁用按钮
	DisableUndoButtonIfNoCache()
	UndoAddOrDelMemberButton.OnTapped = executeUndoMember

	// 创建、配置表单
	form := widget.NewForm()

	// 加载默认配置
	ReloadDefaultPropButton = widget.NewButton("", nil)
	ReloadDefaultPropButton.Text = "重置（加载默认配置）"
	ReloadDefaultPropButton.Icon = theme.ViewRefreshIcon()
	ReloadDefaultPropButton.Importance = widget.WarningImportance
	ReloadDefaultProp := func() {
		Prop, _ = wrap.ReadPropFile(DefaultPropFilePath)
		ApplyToGuiAfterLoadedProp()
		// 通知
		notification := fyne.NewNotification("Loaded", fmt.Sprintf("已加载默认配置：%v", DefaultPropFilePath))
		fyne.CurrentApp().SendNotification(notification)
	}
	ReloadDefaultPropButton.OnTapped = ReloadDefaultProp

	// 保存个人配置
	SaveCustomPropButton = widget.NewButton("", nil)
	SaveCustomPropButton.Text = "保存个人配置"
	SaveCustomPropButton.Icon = theme.DocumentSaveIcon()
	SaveCustomPropButton.Importance = widget.MediumImportance
	SaveCustomProp := func() {

		// 没改变，短路
		if !isCustomPropChanged() {
			return
		}

		// 写入配置
		if err := wrap.WritePropFile(CustomPropPath, Prop); err != nil {
			Notification("Fail", "保存个人配置失败")
		}
		// 赋值缓存
		CustomPropCache = Prop
		// 通知
		notification := fyne.NewNotification("Saved", fmt.Sprintf("已保存保个人配置至：%v", AbsCustomPropPath))
		fyne.CurrentApp().SendNotification(notification)
	}
	SaveCustomPropAndLock := func() { AutoLockCustomPropOpsAndUpdateStatus(SaveCustomProp) }
	SaveCustomPropButton.OnTapped = SaveCustomPropAndLock

	// 加载个人配置
	LoadCustomPropButton = widget.NewButton("", nil)
	LoadCustomPropButton.Text = "加载个人配置"
	LoadCustomPropButton.Importance = widget.WarningImportance
	LoadCustomPropButton.Icon = theme.UploadIcon()
	// 加载个人配置
	LoadCustomProp := func() {
		prop, err := wrap.ReadPropFile(CustomPropPath)
		Prop = prop
		if err != nil {
			Notification("Fail", "加载个人配置失败")
		}
		// 重载配置
		ApplyToGuiAfterLoadedProp()
		// 通知
		Notification("Loaded", fmt.Sprintf("已加载个人配置：%v", AbsCustomPropPath))
	}
	LoadCustomPropAndLock := func() {
		AutoLockCustomPropOpsAndUpdateStatus(LoadCustomProp)
	}
	LoadCustomPropButton.OnTapped = LoadCustomPropAndLock

	// 删除个人配置
	DeleteCustomPropButton = widget.NewButton("", nil)
	DeleteCustomPropButton.Text = "删除个人配置"
	DeleteCustomPropButton.Importance = widget.WarningImportance
	DeleteCustomPropButton.Icon = theme.DeleteIcon()
	DeleteCustomProp := func() {

		// 不存在，短路
		if !isCustomPropFileExist() {
			return
		}

		if err := os.Remove(CustomPropPath); err != nil {
			fmt.Errorf("%v", err)
			Notification("Fail", "删除个人配置失败")
			return
		}
		Notification("Delete", fmt.Sprintln("已删除个人配置：%s", AbsCustomPropPath))
	}
	DeleteCustomPropAndLock := func() {
		AutoLockCustomPropOpsAndUpdateStatus(DeleteCustomProp)
	}
	DeleteCustomPropButton.OnTapped = DeleteCustomPropAndLock

	// 个人配置按钮初始化
	updateCustomPropButtonStatus()

	// 添加表单项
	form.Append("指定月份", MonthSelect)
	form.Append("该月第一个丢垃圾的成员", FirstTRMenember)
	form.Append("该月第一个搞卫生成员", FirstDCMenember)
	form.Append("", layout.NewSpacer())

	// 生成按钮
	generateButton := widget.NewButton("", nil)
	generateButton.Text = "生成Excel"
	generateButton.Importance = widget.HighImportance
	generateButton.Icon = theme.DownloadIcon()
	generateExcelOutPut := func() {
		infiniteWhenGenerating.Show()
		infiniteWhenGenerating.Start()
		generateButton.Hide()

		// 相当于submit 提交成员数据
		//Prop.DailyDuty.Members = Members
		now := time.Now()
		formattedDateTime := now.Format(time.DateOnly)
		outPutPath := fmt.Sprintf(".\\out\\excel-out-%s-%s.xlsx", MonthSelect.Selected, formattedDateTime)
		fmt.Printf("输出路径为：%s\n", outPutPath)
		absOutPath, _ := filepath.Abs(outPutPath)
		// 调用生成Excel的函数 （会创建不存在的目录）
		wrap.GenerateExcel(Prop, "", absOutPath)
		notification := fyne.NewNotification("生成Excel成功", "生成Excel保存至："+absOutPath)

		infiniteWhenGenerating.Stop()
		infiniteWhenGenerating.Hide()
		generateButton.Show()

		//a.SendNotification(notification)
		fyne.CurrentApp().SendNotification(notification)
	}
	generateButton.OnTapped = generateExcelOutPut

	//menuItems := generateMenuItem(5,1)

	// 创建菜单项
	//menuItem1 := fyne.NewMenuItem("Option 1", func() {
	// Option 1 被选中的处理逻辑
	//})
	//menuItem2 := fyne.NewMenuItem("Option 2", func() {
	// Option 2 被选中的处理逻辑
	//})

	//menu := fyne.NewMenu("菜单", []*fyne.MenuItem{menuItem1, menuItem2}...)

	//popUpMenu := widget.NewPopUpMenu(menu, w.Canvas())

	// 使用ScrollContainer包裹列表，以便内容超出窗口时可以滚动
	vScrollMemberList := container.NewVScroll(MembersList)
	vScrollMemberList.SetMinSize(fyne.NewSize(200, 400)) // 设置最小宽度为窗口宽度，高度

	// 添加成员输入框
	var memberToAdd string

	// 数据绑定
	AddMemberEntry = widget.NewEntryWithData(binding.BindString(&memberToAdd))
	AddMemberEntry.PlaceHolder = "输入需要添加的成员"
	// 必须new一个size
	//AddMemberEntry.Resize(fyne.NewSize(80, AddMemberEntry.MinSize().Height))

	// 删除成员输入框
	DeleteMemberSelect = widget.NewSelect(*MembersPtr, nil)

	// 多线程锁
	var MemberListLock sync.Mutex

	// 添加成员按钮  容器
	AddMemberButton = widget.NewButton("Add", nil)
	AddMemberButton.OnTapped = func() {
		MemberListLock.Lock()
		defer MemberListLock.Unlock()
		memberToAdd := AddMemberEntry.Text
		if memberToAdd != "" {
			println("AddMemberEntry.Text:", memberToAdd)
			// 刷新成员
			selfAppendElement(MembersPtr, memberToAdd)
			RefreshAfterMembersChanged()
			// 添加至撤销缓存
			AddUndoMemberCache(memberToAdd, Add)
			AddMemberEntry.SetText("") // 清空输入框
		}
	}

	// 删除成员按钮  容器
	DeleteMemberSelect.PlaceHolder = "选择需要删除的成员"
	DeleteMemberButton = widget.NewButton("Del", nil)
	DeleteMemberButton.OnTapped = func() {
		MemberListLock.Lock()
		defer MemberListLock.Unlock()
		memberToDel := DeleteMemberSelect.Selected
		// 未选择
		if memberToDel == "" {
			return
		}
		if len(*MembersPtr) > 0 {
			selfRemoveElement(MembersPtr, memberToDel)
			RefreshAfterMembersChanged()
			// 清空选择
			DeleteMemberSelect.ClearSelected()
			// 缓存撤销添加或删除成员
			AddUndoMemberCache(memberToDel, Del)
		}
	}

	w.SetContent(
		container.NewHBox(
			container.NewVBox(
				ThemeToggleButton,
				widget.NewLabelWithStyle("成员顺序", fyne.TextAlignCenter,
					fyne.TextStyle{Bold: true}),
				vScrollMemberList),
			container.NewVBox(
				hello, helloButton,
				layout.NewSpacer(),
				form,
				// 回退成员变更
				container.NewGridWithRows(1, UndoAddOrDelMemberButton, ReloadDefaultPropButton),
				container.NewGridWithRows(1, AddMemberEntry, AddMemberButton),
				container.NewGridWithRows(1, DeleteMemberSelect, DeleteMemberButton),
				// 个人配置
				container.NewGridWithRows(1, DeleteCustomPropButton, SaveCustomPropButton, LoadCustomPropButton),
				generateButton,
				infiniteWhenGenerating,
			),
		))

	w.ShowAndRun()
}

func Notification(title string, content string) {
	notification := fyne.NewNotification(title, content)
	fyne.CurrentApp().SendNotification(notification)
}

func generateMenuItem(maxIndex int, excludeIndex int) []fyne.MenuItem {

	// 插入新项的函数
	insertItemAtPosition := func(pos int) {
		println("插入", pos)
	}

	var menuItems []fyne.MenuItem

	for i := 0; i < maxIndex; i++ {
		label := fmt.Sprintf("插入：%v", i)
		menuItem := fyne.NewMenuItem(label, func() {
			insertItemAtPosition(i)
		})
		menuItems = append(menuItems, *menuItem)
	}

	return menuItems
}

// 不检查唯一
func removeElements(members []string, member string) []string {
	var result []string
	for _, v := range members {
		if v != member {
			result = append(result, v)
		}
	}
	return result
}

// 移除单个元素
func removeElement(list []string, str string) []string {
	for i, v := range list {
		if v == str {
			list = append(list[:i], list[i+1:]...)
			return list
		}
	}
	panic("没有这个元素！")
	return list
}

func selfRemoveElement(listPtr *[]string, elem string) {
	for _, v := range *listPtr {
		if v == elem {
			*listPtr = removeElement(*listPtr, elem)
		}
	}
}
func selfAppendElement(listPtr *[]string, elem string) {
	*listPtr = append(*listPtr, elem)
}

func removeElementAt(members []string, index int) []string {
	// 包含从members的起始到index位置之前的元素，以及从index+1位置到最后的元素。
	if index >= 0 && index < len(members) {
		return append(members[:index], members[index+1:]...)
	}
	return members // 如果索引无效，返回原切片
}
